﻿using System;
using System.Collections.Generic;
using System.Xml;
using Jayden.Sc2;

namespace Jayden.Dll.Sc2.Descriptions
{
	public class RaceDescription : Description
	{

		private RaceDictionary m_RaceDictionary;

		private EntityDescription m_Worker;
		private EntityDescription m_Base;
		private EntityDescription m_Supply;
		private EntityDescription m_Vespene;

		private List<EntityDescription> m_StartEntities;
		private HashSet<EntityDescription> m_StartEntitySet;

		private Dictionary<string, EntityDescription> m_Entities;
		private Dictionary<string, ActionDescription> m_Actions;

		public RaceDescription(RaceDictionary dictionary, XmlNode raceNode)
			: base(raceNode)
		{
			m_RaceDictionary = dictionary;
			XmlNode entities = raceNode.SelectSingleNode("Entities");
			if (entities != null)
			{
				m_Entities = new Dictionary<string, EntityDescription>(StringComparer.InvariantCultureIgnoreCase);
				foreach (XmlNode node in entities.ChildNodes)
					if (node is XmlElement)
					{
						EntityDescription entity = NewEntity(node);
						m_Entities.Add(entity.Id, entity);
					}
				m_Worker = this[raceNode.Attributes["worker"].Value];
				m_Base = this[raceNode.Attributes["base"].Value];
				m_Supply = this[raceNode.Attributes["supply"].Value];
				m_Vespene = this[raceNode.Attributes["vespene"].Value];

				m_StartEntities = new List<EntityDescription>();
				XmlNode start = raceNode.SelectSingleNode("Start");
				if (start != null)
					foreach (XmlNode node in start.ChildNodes)
						m_StartEntities.Add(this[node.Attributes["id"].Value]);

				m_Actions = new Dictionary<string, ActionDescription>(StringComparer.InvariantCultureIgnoreCase);
				foreach (EntityDescription entity in m_Entities.Values)
					foreach (ActionDescription action in entity.Actions)
						if (!m_Actions.ContainsKey(action.Id))
							m_Actions.Add(action.Id, action);
			}
		}

		protected virtual EntityDescription NewEntity(XmlNode node) { return new EntityDescription(this, node); }
		public virtual ActionDescription NewAction(EntityDescription entity, XmlNode node)
		{
			switch (node.Name)
			{
				case "Switch":
					return new ResourceActionDescription(entity, node);
				case "Upgrade":
					return new UpgradeActionDescription(entity, node);
				case "Research":
					return new ResearchActionDescription(entity, node);
				case "Cast":
					return new CastActionDescription(entity, node);
				default:
					return new BuildActionDescription(entity, node);
			}
		}

		public RaceDictionary RaceDictionary { get { return m_RaceDictionary; } }

		public EntityDescription this[string entityId]
		{
			get
			{
				EntityDescription entity = null;
				if (m_Entities.TryGetValue(entityId, out entity))
					return entity;
				return null;
			}
		}
		public ActionDescription GetAction(string actionId)
		{
			ActionDescription action = null;
			if (m_Actions.TryGetValue(actionId, out action))
				return action;
			return null;
		}

		public IEnumerable<ActionDescription> Actions { get { return m_Actions.Values; } }
		public IEnumerable<EntityDescription> Entities { get { return m_Entities.Values; } }

		public EntityDescription Worker { get { return m_Worker; } }
		public EntityDescription Base { get { return m_Base; } }
		public EntityDescription Supply { get { return m_Supply; } }
		public EntityDescription Vespene { get { return m_Vespene; } }

		public IEnumerable<EntityDescription> StartEntities { get { return m_StartEntities.AsReadOnly(); } }
		public bool IsStartEntity(EntityDescription descr)
		{
			if (m_StartEntitySet == null)
			{
				m_StartEntitySet = new HashSet<EntityDescription>();
				foreach (EntityDescription e in StartEntities)
					m_StartEntitySet.Add(e);
			}
			return m_StartEntitySet.Contains(descr);
		}

		public EntityDescription GetEntity(string id)
		{
			foreach (EntityDescription entity in Entities)
				if (entity.Id.Equals(id, StringComparison.InvariantCultureIgnoreCase))
					return entity;
			return null;
		}

	}
}
